package com.nrgedge.magnolia.jcaptcha;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.font.FontRenderContext;
import java.awt.font.TextAttribute;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;

import com.octo.captcha.CaptchaException;
import com.octo.captcha.component.image.color.ColorGenerator;
import com.octo.captcha.component.image.color.SingleColorGenerator;
import com.octo.captcha.component.image.textpaster.Glyphs;
import com.octo.captcha.component.image.textpaster.GlyphsPaster;
import com.octo.captcha.component.image.textpaster.glyphsdecorator.GlyphsDecorator;
import com.octo.captcha.component.image.textpaster.glyphsvisitor.GlyphsVisitors;

public class TranslatedGlyphsPaster extends GlyphsPaster {

    private GlyphsVisitors[] glyphVisitors;
    private GlyphsDecorator[] glyphsDecorators;
    private ColorGenerator colorGenerator = new SingleColorGenerator(Color.black);

    public TranslatedGlyphsPaster(Integer minAcceptedWordLength, Integer maxAcceptedWordLength) {
        super(minAcceptedWordLength, maxAcceptedWordLength);
    }

    public TranslatedGlyphsPaster(Integer minAcceptedWordLength, Integer maxAcceptedWordLength, Color textColor) {
        super(minAcceptedWordLength, maxAcceptedWordLength, textColor);
    }

    public TranslatedGlyphsPaster(Integer minAcceptedWordLength, Integer maxAcceptedWordLength, ColorGenerator colorGenerator) {
        super(minAcceptedWordLength, maxAcceptedWordLength, colorGenerator);
        this.colorGenerator = colorGenerator;
    }

    public TranslatedGlyphsPaster(Integer minAcceptedWordLength, Integer maxAcceptedWordLength, ColorGenerator colorGenerator, Boolean manageColorPerGlyph) {
        super(minAcceptedWordLength, maxAcceptedWordLength, colorGenerator, manageColorPerGlyph);
        this.colorGenerator = colorGenerator;
    }

    public TranslatedGlyphsPaster(Integer minAcceptedWordLength, Integer maxAcceptedWordLength, GlyphsVisitors[] glyphVisitors) {
        super(minAcceptedWordLength, maxAcceptedWordLength);
        this.glyphVisitors = glyphVisitors;
    }

    public TranslatedGlyphsPaster(Integer minAcceptedWordLength, Integer maxAcceptedWordLength, Color textColor, GlyphsVisitors[] glyphVisitors) {
        super(minAcceptedWordLength, maxAcceptedWordLength, textColor);
        this.glyphVisitors = glyphVisitors;
    }

    public TranslatedGlyphsPaster(Integer minAcceptedWordLength, Integer maxAcceptedWordLength, ColorGenerator colorGenerator, GlyphsVisitors[] glyphVisitors) {
        super(minAcceptedWordLength, maxAcceptedWordLength, colorGenerator);
        this.glyphVisitors = glyphVisitors;
        this.colorGenerator = colorGenerator;
    }

    public TranslatedGlyphsPaster(Integer minAcceptedWordLength, Integer maxAcceptedWordLength, ColorGenerator colorGenerator, GlyphsVisitors[] glyphVisitors, GlyphsDecorator[] glyphsDecorators) {
        super(minAcceptedWordLength, maxAcceptedWordLength, colorGenerator);
        this.glyphVisitors = glyphVisitors;
        this.glyphsDecorators = glyphsDecorators;
        this.colorGenerator = colorGenerator;
    }

    public TranslatedGlyphsPaster(Integer minAcceptedWordLength, Integer maxAcceptedWordLength, ColorGenerator colorGenerator, Boolean manageColorPerGlyph, GlyphsVisitors[] glyphVisitors) {
        super(minAcceptedWordLength, maxAcceptedWordLength, colorGenerator, manageColorPerGlyph);
        this.glyphVisitors = glyphVisitors;
        this.colorGenerator = colorGenerator;
    }


    public TranslatedGlyphsPaster(Integer minAcceptedWordLength, Integer maxAcceptedWordLength, ColorGenerator colorGenerator, Boolean manageColorPerGlyph, GlyphsVisitors[] glyphVisitors, GlyphsDecorator[] glyphsDecorators) {
        super(minAcceptedWordLength, maxAcceptedWordLength, colorGenerator, manageColorPerGlyph);
        this.glyphVisitors = glyphVisitors;
        this.glyphsDecorators = glyphsDecorators;
        this.colorGenerator = colorGenerator;
    }

    public BufferedImage pasteText(BufferedImage background, AttributedString attributedWord) throws CaptchaException {
        BufferedImage out = copyBackground(background);
        Graphics2D g2 = pasteBackgroundAndSetTextColor(out, background);
        customizeGraphicsRenderingHints(g2);
        
        //build glyphs
        AttributedCharacterIterator acit = attributedWord.getIterator();
        Glyphs glyphs = new Glyphs();
        Glyphs glyphs2 = new Glyphs();

        for(int i = 0 ; i < acit.getEndIndex(); i++){
            Font font = (Font) acit.getAttribute(TextAttribute.FONT);
            g2.setFont(font);
            final FontRenderContext frc = g2.getFontRenderContext();
            glyphs.addGlyphVector(font.createGlyphVector(frc,new char[]{acit.current()}));
            glyphs2.addGlyphVector(font.createGlyphVector(frc,new char[]{acit.current()}));
            acit.next();
        }

        Rectangle2D backgroundBounds = new Rectangle2D.Float(0,0,background.getWidth(),background.getHeight());

        //First decorate
        if (glyphVisitors != null) {
            //System.out.println("\n"+glyphs);
            for (int i = 0; i < glyphVisitors.length; i++) {
                //System.out.println(glyphVisitors[i]);
                glyphVisitors[i].visit(glyphs,backgroundBounds);
                //System.out.println(glyphs);
            }



           /* if(!backgroundBounds.contains(glyphs.getBounds())){

                 StringBuffer error =new StringBuffer("Invalid visitor configuration : " +
                         "\nTrying to move glyphs out of background\n Visitors:");
                for (int i = 0; i < glyphVisitors.length; i++) {
                    error.append("\n");
                    error.append(glyphVisitors[i]);
                }
                 error.append("\nResulting string bounds : ");
                 error.append(glyphs.getBounds());
                 throw new CaptchaException(error.toString());
              }  */
        }

        
        for(int i = 0 ; i < glyphs2.size(); i++){
        	Rectangle2D bounds = glyphs.get(i).getVisualBounds();
        	Rectangle2D bounds2 = glyphs2.get(i).getVisualBounds();
            g2.drawGlyphVector(glyphs2.get(i), (float) (bounds.getX() - bounds2.getX()), (float) (bounds.getY() - bounds2.getY()));
             if(isManageColorPerGlyph())g2.setColor(getColorGenerator().getNextColor());
        }

        //and now decorate
        if (glyphsDecorators != null) {
            for (int i = 0; i < glyphsDecorators.length; i++) {
                glyphsDecorators[i].decorate(g2, glyphs, background);

            }
        }

        g2.dispose();
        return out;
    }


    /**
     * @return the copy of the background
     */
    BufferedImage copyBackground(final BufferedImage background) {
        BufferedImage out = new BufferedImage(background.getWidth(), background.getHeight(),
                background.getType());
        return out;
    }

    /**
     * @param out
     * @param background
     * @return a graphic2D
     */
    Graphics2D pasteBackgroundAndSetTextColor(BufferedImage out, final BufferedImage background) {
        Graphics2D pie = (Graphics2D) out.getGraphics();
        //paste background
        pie.drawImage(background, 0, 0, out.getWidth(), out.getHeight(), null);
        //pie.setColor(getTextColor());
        pie.setColor(colorGenerator.getNextColor());
        return pie;
    }


    void customizeGraphicsRenderingHints(Graphics2D g2){
         g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
                RenderingHints.VALUE_FRACTIONALMETRICS_ON);
        g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
                RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
    }


}